package com.boot.json;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.jackson.JsonComponentModule;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * @author 霜寒 <1621856595@qq.com>
 * @description JSON的工具类
 * @date 2019/9/27 23:31
 **/

@Slf4j
public class JacksonTool {

    private volatile static ObjectMapper om;

    public static ObjectMapper getInstance() {
        if (om == null) {
            synchronized (JacksonTool.class) {
                if (om == null) {
                    om = JacksonCreator.INSTANCE.create();
                }
            }
        }
        return om;
    }

    public JacksonTool(ObjectMapper om) {
        JacksonTool.om = om;
    }

    public static void setObjectMapper(ObjectMapper om) {
        JacksonTool.om = om;
    }

    public static String toJson(Object o) throws JsonProcessingException {
        return getInstance().writeValueAsString(o);
    }

    public static <T> T fromJson(String json, Class<T> clazz) throws JsonProcessingException {
        return getInstance().readValue(json, clazz);
    }

    public static <T> T fromJson(String json, JavaType type) throws JsonProcessingException {
        return getInstance().readValue(json, type);
    }

    public static <T> T fromJson(String json, TypeReference<T> ref) throws JsonProcessingException {
        return getInstance().readValue(json, ref);
    }

    public static <T> T fromJson(byte[] json, Class<T> clazz) throws IOException {
        return getInstance().readValue(json, clazz);
    }

    public static <T> T fromJson(byte[] json, JavaType type) throws IOException {
        return getInstance().readValue(json, type);
    }

    public static <T> T fromJson(byte[] json, TypeReference<T> ref) throws IOException {
        return getInstance().readValue(json, ref);
    }

    public static <T> List<T> fromJsonList(String json, Class<T> clazz) throws JsonProcessingException {
        return getInstance().readValue(json, new TypeReference<List<T>>() {
        });
    }

    public static <T> List<T> fromJsonList(byte[] json, Class<T> clazz) throws IOException {
        return getInstance().readValue(json, new TypeReference<List<T>>() {
        });
    }

    public static <T> Map<String, T> fromJsonMap(String json, Class<T> clazz) throws JsonProcessingException {
        return getInstance().readValue(json, new TypeReference<Map<String, T>>() {
        });
    }

    public static <T> Map<String, T> fromJsonMap(byte[] json, Class<T> clazz) throws IOException {
        return getInstance().readValue(json, new TypeReference<Map<String, T>>() {
        });
    }

    /**
     * @author 霜寒 <1621856595@qq.com>
     * @date 2023/4/25 1:20
     **/

    public static class JacksonCreator {

        private static final DateTimeFormatter DATETIME_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm:ss");

        public static JacksonCreator INSTANCE = new JacksonCreator();

        public ObjectMapper create() {
            Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
            Map<Object, Boolean> features = new HashMap<>();
            features.put(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
            features.put(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS, false);
            features.put(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
            features.put(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS, false);

            features.put(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
            features.put(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT, true);
            features.forEach((feature, value) -> {
                if (value != null) {
                    if (value) {
                        builder.featuresToEnable(feature);
                    } else {
                        builder.featuresToDisable(feature);
                    }
                }
            });
            // default-property-inclusion: non_empty 与自定义 String Serializer 冲突
            builder.serializationInclusion(JsonInclude.Include.NON_EMPTY);
            builder.timeZone(TimeZone.getTimeZone("GMT+8"));
            builder.locale(Locale.SIMPLIFIED_CHINESE);
            builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(DATETIME_FORMAT))
                    .deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(DATETIME_FORMAT))
                    .serializerByType(LocalDate.class, new LocalDateSerializer(DATE_FORMAT))
                    .deserializerByType(LocalDate.class, new LocalDateDeserializer(DATE_FORMAT))
                    .serializerByType(LocalTime.class, new LocalTimeSerializer(TIME_FORMAT))
                    .deserializerByType(LocalTime.class, new LocalTimeDeserializer(TIME_FORMAT))
                    .serializerByType(Long.class, ToStringSerializer.instance);
            //.serializerByType(String.class, StringToNullSerializer.INSTANCE)
            //.deserializerByType(String.class, StringToNullDeserializer.INSTANCE);

            builder.propertyNamingStrategy(PropertyNamingStrategies.LOWER_CAMEL_CASE);

            builder.modulesToInstall(new ParameterNamesModule(JsonCreator.Mode.DEFAULT), new JsonComponentModule());
            return builder.createXmlMapper(false).build();
        }

    }
}
